package com.xyy.saas.payment.api.impl;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigChangeListener;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.google.common.util.concurrent.RateLimiter;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.alibaba.dubbo.config.annotation.Service;
import com.alibaba.fastjson.JSON;
import com.pingplusplus.Pingpp;
import com.pingplusplus.model.Identification;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.cores.api.UserInfoAuthApi;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.cores.vo.UserInfoAuthVo;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.JSONUtils;
import com.xyy.saas.payment.util.MatcherUtil;
import com.xyy.saas.payment.util.SignUtil;
import com.xyy.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.DependsOn;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Author: wuqiongda
 * @Date: 2020/2/18 17:10
 */
@DependsOn({"apolloUtil"})
@Service(version = "1.0.0", loadbalance = "roundrobin")
public class UserInfoAuthApiImpl implements UserInfoAuthApi, InitializingBean {

    private static final Logger LOGGER = LoggerFactory.getLogger(UserInfoAuthApiImpl.class);

    private int rateLimiterCreate = Integer.parseInt(ApolloUtil.getProperty(Constants.IDENTIFY_AUTH_LIMIT_CRATE));

    private RateLimiter rateLimiter = RateLimiter.create(rateLimiterCreate);


    /**
     * 认证银行卡信息
     *
     * 参考文档：https://www.pingxx.com/api#请求认证接口
     */
    @Override
    public ResultVO identify(UserInfoAuthVo userInfoAuthVo, String sign) {
        // 尝试获取令牌
        if(!rateLimiter.tryAcquire(1)) {
            LOGGER.warn("UserInfoAuthApiImpl refuse identify userInfoAuthVo:{}", JSON.toJSON(userInfoAuthVo));
            return ResultVO.create(ResultCodeEnum.SYSTEM_BUSY_ERROR);
        }
        if (!isVerification(userInfoAuthVo)){
            return ResultVO.createParamError();
        }
        Map<String, Object> map = new HashMap<>();
        map.put("identifyTypeEnum", userInfoAuthVo.getIdentifyTypeEnum());
        map.put("name", userInfoAuthVo.getName());
        map.put("idCard", userInfoAuthVo.getIdCard());
        map.put("invoker", userInfoAuthVo.getInvoker());
        if(!StringUtil.isEmpty(userInfoAuthVo.getBankCard())) {
            map.put("bankCard", userInfoAuthVo.getBankCard());
        }
        LOGGER.info("UserInfoAuthApiImpl#identify signMap:{}", JSON.toJSONString(map));
        String publicKey = ApolloUtil.getProperty(userInfoAuthVo.getInvoker().getType() + Constants.PUBLIC);
        if (!SignUtil.doCheck(map, sign, publicKey)) {
            return ResultVO.createSignError();
        }
        ResultVO resultVO = new ResultVO();
        try {
            Map<String, Object> params = new HashMap<String, Object>(8);
            params.put("app", Pingpp.appId);
            params.put("type", userInfoAuthVo.getIdentifyTypeEnum().getType());
            Map<String, String> data = new HashMap<String, String>(8);
            data.put("id_name", userInfoAuthVo.getName());
            data.put("id_number", userInfoAuthVo.getIdCard());
            if (!StringUtil.isEmpty(userInfoAuthVo.getBankCard())){
                data.put("card_number", userInfoAuthVo.getBankCard());
            }
            params.put("data", data);
            Identification result = Identification.identify(params);
            if (result.getResultCode() == 0) {
                LOGGER.info("UserInfoAuthApiImpl identifyBankCard authentication pass userInfoAuthVo{}", JSONUtils.toJSON(userInfoAuthVo));
                return ResultVO.createSuccess();
            } else {
                LOGGER.info("UserInfoAuthApiImpl identify authentication fail userInfoAuthVo{}", JSONUtils.toJSON(userInfoAuthVo));
                resultVO.setCode(result.getResultCode().toString());
                resultVO.setMsg(result.getMessage());
                return resultVO;
            }
        } catch (Exception e) {
            LOGGER.error("UserInfoAuthApiImpl identify fail userInfoAuthVo{}", JSONUtils.toJSON(userInfoAuthVo), e);
            return ResultVO.createError("identify fail");
        }
    }

    private boolean isVerification(UserInfoAuthVo userInfoAuthVo){
        if (StringUtil.isEmpty(userInfoAuthVo.getName())||StringUtil.isEmpty(userInfoAuthVo.getIdCard())||userInfoAuthVo.getInvoker()==null){
            LOGGER.warn("UserInfoAuthApiImpl#isVerification param is empty userInfoAuthVo:{}", JSONUtils.toJSON(userInfoAuthVo));
            return false;
        }
        if (Constants.BANK_CARD.equals(userInfoAuthVo.getIdentifyTypeEnum().getType())){
            if (StringUtil.isEmpty(userInfoAuthVo.getBankCard())){
                LOGGER.warn("UserInfoAuthApiImpl#isVerification bank card is empty userInfoAuthVo:{}", JSONUtils.toJSON(userInfoAuthVo));
                return false;
            }
            if (!MatcherUtil.isValidBankCard(userInfoAuthVo.getBankCard())){
                LOGGER.warn("UserInfoAuthApiImpl#isVerification bank card is not valid userInfoAuthVo:{}", JSONUtils.toJSON(userInfoAuthVo));
                return false;
            }
        }
        if (Constants.ID_CARD.equals(userInfoAuthVo.getIdentifyTypeEnum().getType())){
            if (!StringUtil.isEmpty(userInfoAuthVo.getBankCard())){
                LOGGER.warn("UserInfoAuthApiImpl#isVerification bank card is not empty userInfoAuthVo:{}", JSONUtils.toJSON(userInfoAuthVo));
                return false;
            }
        }
        if (!MatcherUtil.isValidIdCard(userInfoAuthVo.getIdCard())||MatcherUtil.isHasSpecialSymbol(userInfoAuthVo.getName())){
            LOGGER.warn("UserInfoAuthApiImpl#isVerification ID card is not valid userInfoAuthVo:{}", JSONUtils.toJSON(userInfoAuthVo));
            return false;
        }
        return true;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Set set = new HashSet();
        set.add(Constants.IDENTIFY_AUTH_LIMIT_CRATE);
        //动态监听配置变化
        Config config = ConfigService.getAppConfig();
        config.addChangeListener(new ConfigChangeListener() {
            @Override
            public void onChange(ConfigChangeEvent changeEvent) {
                ConfigChange change = changeEvent.getChange(Constants.IDENTIFY_AUTH_LIMIT_CRATE);
                rateLimiter = RateLimiter.create(Integer.parseInt(change.getNewValue()));
            }
        }, set);
    }
}